/* ===================

AX2E (AXESCON XACML 2.0 Engine) is the Java authorization engine, which implements OASIS XACML 2.0 standard.
Copyright (C) 2007 AXESCON LLC

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


Contact: AXESCON LLC - info{at}axescon.com

 =================== */
package axescon.xacml.engine;

import axescon.xacml.api.*;
import axescon.xacml.model.ctx.Attribute;
import axescon.xacml.model.ctx.XacmlCtx;
import axescon.xacml.model.ctx.StatusCode;
import axescon.xacml.model.ctx.impl.StatusImpl;
import axescon.xacml.model.policy.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.lang.reflect.Array;

/**
 * @author argyn
 *         Date: Jun 25, 2005
 *         Time: 2:16:46 PM
 *         Responsibilities: represents XACML PDP
 */
public class ExpressionEvaluatorImpl implements ExpressionEvaluator {

    public ExpressionEvaluatorImpl() throws XacmlException {
        this(false);
    }

    public ExpressionEvaluatorImpl(boolean setDefaults) throws XacmlException {
        if (setDefaults) setFnFactory(new BaseFnFactory(true));
    }

    /**
     * see axescon.xacml 2.0 spec ch. 5.37. Complex type AttributeDesignatorType
     *
     * @param x
     * @param ch
     * @param req
     * @return
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public AttributeValue[] evalEnvironment(AttributeDesignator x, CtxHandler ch, XacmlCtx req)
            throws XacmlException, ExpressionEvalIndeterminateException {
        assert x != null: "Null Environment AttributeDesignator passed to Expression Evaluator";

        Attribute[] al = ch.getEnvironmentAttributeArray(req);
        AttributeValue[] ret = eval(x, al);
        return ret;
    }

    /**
     * see axescon.xacml 2.0 spec ch. 5.37. Complex type AttributeDesignatorType
     *
     * @param x
     * @param ch
     * @param req
     * @return return array of attribute values
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public AttributeValue[] evalResource(AttributeDesignator x, CtxHandler ch, XacmlCtx req)
            throws ExpressionEvalIndeterminateException, XacmlException {
        assert x != null: "Null Resource AttributeDesignator passed to Expression Evaluator";

        Attribute[] al = ch.getResourceAttributeArray(req);
        AttributeValue[] ret = eval(x, al);
        return ret;
    }

    /**
     * see axescon.xacml 2.0 spec ch. 5.37. Complex type AttributeDesignatorType
     *
     * @param x
     * @param ch
     * @param req
     * @return
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public AttributeValue[] evalAction(AttributeDesignator x, CtxHandler ch, XacmlCtx req)
            throws ExpressionEvalIndeterminateException, XacmlException {
        assert x != null: "Null Action AttributeDesignator passed to Expression Evaluator";

        Attribute[] al = ch.getActionAttributeArray(req);
        AttributeValue[] ret = eval(x, al);
        return ret;
    }

    /**
     * see axescon.xacml 2.0 spec ch. 5.37. Complex type AttributeDesignatorType
     *
     * @param x
     * @param ch
     * @param req
     * @return
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public AttributeValue[] evalSubject(SubjectAttributeDesignator x, CtxHandler ch, XacmlCtx req)
            throws ExpressionEvalIndeterminateException, XacmlException {
        assert x != null: "Null Subject AttributeDesignator passed to Expression Evaluator";

        Attribute[] al = ch.getSubjectAttributeArray(x.getAttributeId(), x.getSubjectCategory(), req);
        AttributeValue[] ret = eval(x, al);
        return ret;
    }

    /**
     * @param x
     * @param attrs
     * @return
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public AttributeValue[] eval(AttributeDesignator x, Attribute[] attrs)
            throws ExpressionEvalIndeterminateException, XacmlException {

        assert x != null: "Null AttributeDesignator passed to Expression Evaluator";

        //ArrayList<axescon.xacml.model.ctx.AttributeValue> ret = new ArrayList<axescon.xacml.model.ctx.AttributeValue>();

        ArrayList ret = null;
        for (int i = 0; attrs != null && i < attrs.length; i++) {
            Attribute a = attrs[i];
            if (!x.getAttributeId().equals(a.getAttributeId())) continue;
            if (!x.getDataType().equals(a.getDataType())) continue;
            if (x.getIssuer() != null && !x.getIssuer().equals(a.getIssuer())) continue;

            if (ret == null) ret = new ArrayList();
            ret.addAll(Arrays.asList(a.getAttributeValueArray()));
        }

        if ((ret == null|| ret.size() == 0) && x.isMustBePresent()){
            ExpressionEvalIndeterminateException missingAttrException =
                new ExpressionEvalIndeterminateException("Expression Evaluator cant evaluate AttributeDesignator: " + x
                        + " to Attribute.");
            missingAttrException.setStatus(new StatusImpl(StatusCode.INT_MISSING_ATTRIBUTE));
            throw missingAttrException;
        }

        // empty array can be null 
        if(ret == null) return null;

        // by this point we know that ret is not empty
        return (AttributeValue[]) ret.toArray((AttributeValue[])Array.newInstance(ret.get(0).getClass(),ret.size()));

    }


    public Liquid evalApply(Apply x, CtxHandler ch, XacmlCtx req)
            throws XacmlException, ExpressionEvalIndeterminateException {
        String funcId = x.getFunctionId();
        Liquid ret = getFnFactory().exec(funcId, x.getExpressionArray()
                , ch, req, this);
        return ret;
    }

    private FnFactory fnFactory;


    public FnFactory getFnFactory() {
        return fnFactory;
    }

    public void setFnFactory(FnFactory fnFactory) {
        this.fnFactory = fnFactory;
    }

    /**
     * evaluates expressions to 3d array of attribute values
     *
     * @param exprs
     * @param ch
     * @param req
     * @return
     * @throws ExpressionEvalIndeterminateException
     *
     */
    public Liquid[] eval(Expression[] exprs, CtxHandler ch, XacmlCtx req) throws
            XacmlException, ExpressionEvalIndeterminateException {
        if (exprs == null) return null;
        Liquid[] ret = new Liquid[exprs.length];
        for (int i = 0; i < exprs.length; i++) {
            ret[i] = exprs[i].eval(ch, req, this);
        }
        return ret;

    }

    private AttributeSelectorEvaluator attributeSelectorEvaluator;

    public AttributeSelectorEvaluator getAttributeSelectorEvaluator() {
        return attributeSelectorEvaluator;
    }

    public void setAttributeSelectorEvaluator(AttributeSelectorEvaluator attributeSelectorEvaluator) {
        this.attributeSelectorEvaluator = attributeSelectorEvaluator;
    }

}
